package it.polito.connectedcar.general.network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * Implementation of a socket Provider. It is used in a simulation environment
 * to simulate delays of the network. Whenever a message has to be sent , a
 * ScheduledThreadExecutor execute the real message sending after a random delay
 * 
 * @author Nicola Aresta
 * 
 */
public class SimulatedDatagramSocket extends ConnectedCarSocket {

	private ScheduledExecutorService executorService;
	private final Random delayRandom = new Random(System.currentTimeMillis());

	public SimulatedDatagramSocket() throws SocketException {
		super();
		init();
	}

	public SimulatedDatagramSocket(int port) throws SocketException {
		super(port);
		init();
	}

	public SimulatedDatagramSocket(SocketAddress address)
			throws SocketException {
		super(address);
		init();
	}

	/**
	 * This method actually enqueue the execution of a message applying a random
	 * delay. When the delay expires, the Runnable is executed and the message
	 * is sent
	 */
	@Override
	public void sendMessage(final DatagramPacket p) throws IOException {
		executorService.schedule(new Runnable() {

			@Override
			public void run() {
				/*
				 * send the Message to the underlying network service in order
				 * to be transmitted
				 */
				try {
					send(p);

				} catch (IOException e) {
					e.printStackTrace();
				}

			}
		}, getSimulatedDelay(), TimeUnit.MILLISECONDS);
	}

	private void init() {
		executorService = Executors.newSingleThreadScheduledExecutor();
	}

	private long getSimulatedDelay() {
		return delayRandom.nextLong();
	}

	@Override
	public boolean isSameAddress(InetSocketAddress address) {
		return address.getPort() == getLocalPort();
	}
}
